package cn.funeralobjects.upms.service.impl;

import cn.funeralobjects.common.service.annotataion.CommonRollback;
import cn.funeralobjects.common.service.annotataion.CommonService;
import cn.funeralobjects.common.service.exception.OperationException;
import cn.funeralobjects.upms.entity.EApiRole;
import cn.funeralobjects.upms.entity.EUserGroup;
import cn.funeralobjects.upms.entity.EUserRole;
import cn.funeralobjects.upms.repository.ApiRoleRepository;
import cn.funeralobjects.upms.repository.UserGroupRepository;
import cn.funeralobjects.upms.repository.UserRoleRepository;
import cn.funeralobjects.upms.service.*;
import cn.funeralobjects.util.annotation.ArgNotEmpty;
import cn.funeralobjects.util.annotation.ArgNotZero;
import cn.funeralobjects.util.annotation.AssertArg;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * PermissionService的实现
 *
 * @author FuneralObjects
 * Create date: 2020/4/24 4:21 PM
 */
@CommonService
public class PermissionServiceImpl implements PermissionService {
    @Resource
    private UserRoleRepository userRoleRepository;

    @Resource
    private UserGroupRepository userGroupRepository;

    @Resource
    private ApiRoleRepository apiRoleRepository;

    @Resource
    private UserService userService;

    @Resource
    private GroupService groupService;

    @Resource
    private RoleService roleService;

    @Resource
    private ApiService apiService;

    @CommonRollback
    @Override
    public void addUserToGroup(Collection<Integer> userIds, Integer groupId) {
        mustExistsAllUserId(userIds);
        groupService.mustExistsById(groupId);
        userGroupRepository.saveAll(userIds.stream().distinct().map(userId -> new EUserGroup().setGroupId(groupId).setUserId(userId)).collect(Collectors.toSet()));
    }

    @CommonRollback
    @Override
    public void addUserToGroup(Integer userId, Integer groupId) {
        userService.mustExistsById(userId);
        groupService.mustExistsById(groupId);
        userGroupRepository.save(new EUserGroup().setUserId(userId).setGroupId(groupId));
    }

    @CommonRollback
    @Override
    public void addUserToGroup(Integer userId, Collection<Integer> groupIds) {
        mustExistsAllGroupId(groupIds);
        userService.mustExistsById(userId);
        userGroupRepository.saveAll(groupIds.stream().map(groupId -> new EUserGroup().setUserId(userId).setGroupId(groupId)).collect(Collectors.toSet()));
    }

    @CommonRollback
    @Override
    public void addRoleToUser(Collection<Integer> roleIds, Integer userId) {
        mustExistsAllRoleId(roleIds);
        userService.mustExistsById(userId);
        userRoleRepository.saveAll(roleIds.stream().map(roleId -> new EUserRole().setRoleId(roleId).setUserId(userId)).collect(Collectors.toSet()));
    }

    @CommonRollback
    @Override
    public void addRoleToUser(Integer roleId, Integer userId) {
        userService.mustExistsById(userId);
        roleService.mustExistsById(roleId);
        userRoleRepository.save(new EUserRole().setRoleId(roleId).setUserId(userId));
    }

    @CommonRollback
    @Override
    public void addRoleToUser(Integer roleId, Collection<Integer> userIds) {
        roleService.mustExistsById(roleId);
        mustExistsAllUserId(userIds);
        userRoleRepository.saveAll(userIds.stream().map(userId -> new EUserRole().setRoleId(roleId).setUserId(userId)).collect(Collectors.toSet()));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeRoleFromUser(@ArgNotZero Integer userId, @ArgNotZero Integer roleId) {
        userRoleRepository.delete(new EUserRole().setRoleId(roleId).setUserId(userId));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeAllRolesFromUser(@ArgNotZero Integer userId) {
        userRoleRepository.deleteAllByUserId(userId);
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeAllUsersFromRole(@ArgNotZero Integer roleId) {
        userRoleRepository.deleteAllByRoleId(roleId);
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeUserFromGroup(@ArgNotZero Integer groupId, @ArgNotZero Integer userId) {
        userGroupRepository.delete(new EUserGroup().setGroupId(groupId).setUserId(userId));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeAllGroupsFromUser(@ArgNotZero Integer userId) {
        userGroupRepository.deleteAllByUserId(userId);
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeAllUsersFromGroup(@ArgNotZero Integer groupId) {
        userGroupRepository.deleteAllByGroupId(groupId);
    }

    @CommonRollback
    @Override
    public void addRoleToApi(Integer roleId, Integer apiId) {
        roleService.mustExistsById(roleId);
        apiService.mustExistsById(apiId);
        apiRoleRepository.save(new EApiRole().setRoleId(roleId).setApiId(apiId));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeRoleFromApi(@ArgNotZero Integer roleId, @ArgNotZero Integer apiId) {
        apiRoleRepository.delete(new EApiRole().setApiId(apiId).setRoleId(roleId));
    }

    @CommonRollback
    @AssertArg
    @Override
    public void removeAllRolesFromApi(@ArgNotZero Integer apiId) {
        apiRoleRepository.deleteAllByApiId(apiId);
    }

    @AssertArg
    @Override
    public boolean existsRoleFromUser(@ArgNotZero Integer userId, @ArgNotZero Integer roleId) {
        return userRoleRepository.existsById(new EUserRole().setUserId(userId).setRoleId(roleId));
    }

    @AssertArg
    @Override
    public boolean existsAllRolesFromUser(@ArgNotZero Integer userId, Collection<Integer> roleIds) {
        roleService.validateId(roleIds);
        return userRoleRepository.filterRoleIdByUserId(userId, roleIds).containsAll(roleIds);
    }

    @AssertArg
    @Override
    public boolean existsAllUsersFromRole(@ArgNotZero Integer roleId, Collection<Integer> userIds) {
        userService.validateId(userIds);
        return userRoleRepository.filterUserIdByRoleId(roleId, userIds).containsAll(userIds);
    }

    @AssertArg
    @Override
    public boolean existsUserFromGroup(@ArgNotZero Integer groupId, @ArgNotZero Integer userId) {
        return userGroupRepository.existsById(new EUserGroup().setGroupId(groupId).setUserId(userId));
    }

    @AssertArg
    @Override
    public boolean existsAllUsersFromGroup(@ArgNotZero Integer groupId, Collection<Integer> userIds) {
        userService.validateId(userIds);
        return userGroupRepository.filterUserIdByGroupId(groupId, userIds).containsAll(userIds);
    }

    @Override
    public boolean existsAllGroupsFromUser(Integer userId, Collection<Integer> groupIds) {
        groupService.validateId(groupIds);
        return userGroupRepository.filterGroupIdByUserId(userId, groupIds).containsAll(groupIds);
    }

    @AssertArg
    @Override
    public boolean existsRoleFromApi(@ArgNotZero Integer roleId, @ArgNotZero Integer apiId) {
        return apiRoleRepository.existsById(new EApiRole().setApiId(apiId).setRoleId(roleId));
    }

    @AssertArg
    @Override
    public boolean existsAllRolesFromApi(@ArgNotZero Integer apiId, Collection<Integer> roleIds) {
        roleService.validateId(roleIds);
        return apiRoleRepository.filterRoleByApi(apiId, roleIds).containsAll(roleIds);
    }

    @AssertArg
    @Override
    public boolean existsAllApiFromRole(@ArgNotZero Integer roleId, Collection<Integer> apiIds) {
        apiService.validateId(apiIds);
        return apiRoleRepository.filterApiByRole(roleId, apiIds).containsAll(apiIds);
    }

    @AssertArg
    @Override
    public List<Integer> findUserIdFromRoleId(@ArgNotZero Integer roleId) {
        roleService.validateId(roleId);
        return userRoleRepository.findAllByRoleId(roleId).stream().map(EUserRole::getUserId).collect(Collectors.toList());
    }

    @AssertArg
    @Override
    public List<Integer> findRoleIdFromUserId(@ArgNotZero Integer userId) {
        userService.validateId(userId);
        return userRoleRepository.findAllByUserId(userId).stream().map(EUserRole::getRoleId).collect(Collectors.toList());
    }

    @AssertArg
    @Override
    public List<Integer> findApiIdFromRoleId(@ArgNotZero Integer roleId) {
        roleService.validateId(roleId);
        return apiRoleRepository.findAllByRoleId(roleId).stream().map(EApiRole::getApiId).collect(Collectors.toList());
    }

    @AssertArg
    private void mustExistsAllUserId(@ArgNotEmpty Collection<Integer> userIds) {
        if (!userService.existsById(userIds)) {
            throw new OperationException("There are some userId that do not exists.");
        }
    }

    @AssertArg
    private void mustExistsAllGroupId(@ArgNotEmpty Collection<Integer> groupIds) {
        if (!groupService.existsById(groupIds)) {
            throw new OperationException("There are some groupIds that do not exists.");
        }
    }

    @AssertArg
    private void mustExistsAllRoleId(@ArgNotEmpty Collection<Integer> roleIds) {
        if (!roleService.existsById(roleIds)) {
            throw new OperationException("There are some roleIds that do not exists.");
        }
    }
}